# _*_ coding:utf-8 _*_
# @Time  : 2022.06.21
# @Author: zizlee
import datetime
import json
import sys
import time

import requests
import urllib3
from WindPy import w

urllib3.disable_warnings()


# 进度打印
def print_progress_bar(current, total, color=35, title=''):  # percent是小数
    percent = round(current/total, 4)
    prefix_progress = '▓' * int(80 * percent)
    leave_progress = '▓' * (80 - int(80 * percent))
    # percent = '%.2f' % (percent * 100)
    print(f'\033[1;{color}m{current}/{total}\033[0m|\033[1;{color}m{prefix_progress}\033[0m{leave_progress} {title}')


# 获取各品种的主力合约代码
def get_variety_main_contract(query_date):
    url = 'https://210.13.218.130:9000/v1/ruizy/variety/contract/'
    query = {'m': 1, 'date': query_date}
    r = requests.get(url, params=query, verify=False)
    contract_data = r.json()['data']
    return {c['variety_en']: c['scontract'] for c in contract_data}


# 查询数据指标的具体数据
def get_index_information(index_id, page, page_size, use_max=True, use_day=None):
    query = {
        'iid': index_id,
        'page': page,
        'page_size': page_size
    }
    url = 'https://210.13.218.130:9000/v1/datalib/table/info/'
    r = requests.get(url, params=query, verify=False)
    resp_data = r.json()['data']
    # index_info = resp_data['index_info']
    index_data = resp_data['data']
    value = None
    if not index_data:
        return value
    if use_max:
        index_data = sorted(index_data, key=lambda x: x['datadate'], reverse=True)
        value = index_data[0]['datavalue']
    if use_day:
        value = None
        for i_data in index_data:
            if i_data['datadate'] == use_day:
                value = i_data['datavalue']
                break
    return value


# 获取隐含波动率的平值期权代码
def get_implied_wd_code(index_item, contracts_dict, query_day):
    contract = contracts_dict.get(index_item['variety_en'])
    value = get_index_information(index_id=index_item['ZLID'], page=1, page_size=10, use_max=True, use_day=query_day)
    if not all([contract, value]):
        raise ValueError('没有合约或价格数据!', contract, value)

    # 计算期权平值
    option_value = None
    for rule in index_item['price_rules']:
        if rule['min'] < value <= rule['max']:
            option_value = int(round(value / rule['interval'], 0) * rule['interval'])
            break
    if not option_value:
        raise ValueError('平值期权代码计算错误:', option_value)
    # if index_item['variety_en'] == 'CF':  # 调整合约代码使用
    #     option_value = 17400
    # 期权代码
    option_code = f'{index_item["variety_en"]}{contract}'
    option_code += f'{index_item["interval_symbol"]}{index_item["direction"]}{index_item["interval_symbol"]}'
    option_code += f'{option_value}.{index_item["exchange"]}'
    return option_code


# 提取结果数据
def extra_result_data(ret, index_item):
    if ret.ErrorCode != 0:
        print(f'获取{index_item["source_id"]}:{index_item["name_zh"]}数据失败了!', ret.ErrorCode)
        return []
    if len(ret.Data[0]) == 1 and ret.Data[0][0] is None:
        return []
    if str(ret.ErrorCode) == str(-40520007):
        print(f'{index_item["source_id"]}:{index_item["name_zh"]}没有可用数据！')
        return []
    if index_item['flag'] == 'us_impliedvol':  # 隐含波动率
        data_dates = [t.strftime('%Y-%m-%d') for t in ret.Times]
        data_values = [round(v * 100, 2) for v in ret.Data[0]]
        ret_data = [{'datadate': tp[0], 'datavalue': tp[1]} for tp in list(zip(data_dates, data_values))]

    elif index_item['flag'] == 'volatilityratio':  # 历史波动率
        data_dates = [t.strftime('%Y-%m-%d') for t in ret.Times]
        data_values = [round(v, 4) for v in ret.Data[0]]
        ret_data = [{'datadate': tp[0], 'datavalue': tp[1]} for tp in list(zip(data_dates, data_values))]
    else:
        ret_data = []
    return ret_data


# 获取隐含波动率
def handle_volatility_ratio(indexes, contracts_dict):
    total_count = len(indexes)
    for i, index_item in enumerate(indexes):
        # 看是历史波动率还是期权隐含波动率指标
        start_date = START if START else datetime.datetime.today().strftime('%Y-%m-%d')
        # if index_item['id'] == 3962:
        #     start_date = '2022-10-11'  # 动力煤20日历史波动率一直无数据，不更新了，有了一起更新
        #     print(start_date, index_item)

        if index_item['flag'] == 'us_impliedvol':  # 隐含波动率,仅能获取当天的数据才是准确的
            wd_code = get_implied_wd_code(index_item, contracts_dict, query_day=start_date)
            # 获取Wind平值期权隐含波动率数据
            # if wd_code == 'ZC209C860.CZC':  # 主力合约还没换过来时处理
            #     wd_code = 'ZC210C860.CZC'
            # if wd_code == 'ZC209P860.CZC':
            #     wd_code = 'ZC210P860.CZC'

            ret = w.wsd(wd_code, 'us_impliedvol', start_date, END_DATE)
            wd_code = wd_code
        elif index_item['flag'] == 'volatilityratio':  # 历史波动率
            if index_item['start']:
                start_date = index_item['start']
            ret = w.wsd(index_item['wd_code'], 'volatilityratio', start_date, END_DATE,
                        f"CalculationTime={index_item['day_count']}", f"AnnualCoefficient={ANNUAL_NUMBER}")
            wd_code = index_item['wd_code']
        else:
            raise ValueError('不支持该数据获取!', index_item['flag'])
        # print(ret)
        ret_data = extra_result_data(ret, index_item)
        print(wd_code, ret_data)
        if not ret_data:
            continue
        save_data_to_server(index_item['id'], datalist=ret_data)
        # print(index_item["name_zh"], ret_data)
        # 打印进度
        msg = f'{index_item["source_id"]}:{index_item["name_zh"]},起始日期:{start_date}保存完成.'
        print_progress_bar(current=i + 1, total=total_count, color=35, title=msg)
        time.sleep(0.5)


# 保存数据到指标中
def save_data_to_server(sheet_id, datalist):
    url = 'https://210.13.218.130:9000/v1/datalib/table/update/'
    body_data = {
        'dataid': sheet_id,
        'row_value': datalist
    }
    try:
        r = requests.post(url, json=body_data, verify=False)
        if r.json()['code'] != 200:
            raise ValueError(f'status_code={r.json()["code"]}')
    except Exception as e:
        if datalist:
            msg = f'保存数据指标 ID={sheet_id}失败了：{e}'
        else:
            msg = f'更新指标 ID={sheet_id} 刷新日期失败了：{e}'
        print(msg)
    else:
        print(r.json())


def read_index_configs():
    with open('波动率.json', 'r', encoding='utf8') as fp:
        indexes = json.load(fp)
    print('读取更新数量:', len(indexes))
    if POINTERS:
        indexes = list(filter(lambda x: x['source_id'] in POINTERS, indexes))

    print('等待更新数量:', len(indexes))

    return indexes


if __name__ == '__main__':
    # while True:
    #     if datetime.datetime.now().strftime('%H%M') in ['1640']:
    ANNUAL_NUMBER = 252  # 年化系数
    START = ''  # 开始日期，若历史波动率配置文件中自己有配置，则依其配置2022-08-16
    END_DATE = datetime.datetime.today().strftime('%Y-%m-%d')  # datetime.datetime.today().strftime('%Y-%m-%d')
    # END_DATE = '2022-12-26'
    POINTERS = []  # 指定更新 'R00020697', 'R00020698', 'R00020699', 'R00020700', 'R00020701',（ZC.CZC）
    # 读取配置信息
    index_list = read_index_configs()
    # index_list = list(filter(lambda x: x['id'] > 3960, index_list))  # SC>=3880, #PG>=3910,#ZC>3960，SR>=3940
    index_list = list(filter(lambda x: x['flag'] == 'us_impliedvol', index_list))   # 只更新期权
    # index_list = list(filter(lambda x: x['variety_en'] in ['SC'], index_list))
    # 查询合约数据
    contract_date = datetime.datetime.today() + datetime.timedelta(days=1)
    # contract_date = datetime.datetime.strptime('20221220', '%Y%m%d')
    variety_contracts = get_variety_main_contract(query_date=contract_date.strftime('%Y%m%d'))  # 多给一天才能正确获取当日的主力合约

    if variety_contracts.get('SC') == '2302':
        variety_contracts['SC'] = '2303'
    if variety_contracts.get('L') == '2301':
        variety_contracts['L'] = '2305'
    if variety_contracts.get('V') == '2301':
        variety_contracts['V'] = '2305'
    if variety_contracts.get('P') == '2301':
        variety_contracts['P'] = '2302'
    if variety_contracts.get('PG') == '2301':
        variety_contracts['PG'] = '2302'
    if variety_contracts.get('PP') == '2301':
        variety_contracts['PP'] = '2305'
    if variety_contracts.get('MA') == '301':
        variety_contracts['MA'] = '302'
    if variety_contracts.get('RM') == '301':
        variety_contracts['RM'] = '303'
    if variety_contracts.get('ZC') == '301':
        variety_contracts['ZC'] = '302'
    if variety_contracts.get('M') == '2301':
        variety_contracts['M'] = '2303'
    if variety_contracts.get('CU') == '2301':
        variety_contracts['CU'] = '2302'

    w.start()  # 开启windAPI
    handle_volatility_ratio(indexes=index_list, contracts_dict=variety_contracts)
    w.stop()
    time.sleep(1)
